package com.linkq.ipinyin.utils;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.io.IOException;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author wangpeng
 * @since 2025-06-30
 **/

public class ApplePublicKeyFetcher {
    private static final String APPLE_PUBLIC_KEY_URL = "https://appleid.apple.com/auth/keys";
    private static final Map<String, PublicKey> PUBLIC_KEY_CACHE = new ConcurrentHashMap<>();
    private static long lastFetchTime = 0;
    private static final long CACHE_EXPIRY_MS = 24 * 60 * 60 * 1000; // 24小时缓存

    /**
     * 获取 Apple 公钥（带缓存机制）
     * @param keyId 密钥ID（kid）
     * @return 对应的 PublicKey
     */
    public static PublicKey fetchApplePublicKey(String keyId) throws Exception {
        // 检查缓存是否有效
        if (System.currentTimeMillis() - lastFetchTime > CACHE_EXPIRY_MS) {
            PUBLIC_KEY_CACHE.clear(); // 缓存过期，清空
        }

        // 如果缓存中有，直接返回
        if (PUBLIC_KEY_CACHE.containsKey(keyId)) {
            return PUBLIC_KEY_CACHE.get(keyId);
        }

        // 否则从 Apple 服务器获取
        List<Map<String, Object>> keys = fetchApplePublicKeys();
        for (Map<String, Object> keyInfo : keys) {
            String currentKid = (String) keyInfo.get("kid");
            if (keyId.equals(currentKid)) {
                PublicKey publicKey = parsePublicKey(keyInfo);
                PUBLIC_KEY_CACHE.put(keyId, publicKey);
                lastFetchTime = System.currentTimeMillis();
                return publicKey;
            }
        }

        throw new IllegalArgumentException("No matching public key found for kid: " + keyId);
    }

    /**
     * 从 Apple 服务器获取公钥列表（JDK 8 使用 HttpURLConnection）
     */
    private static List<Map<String, Object>> fetchApplePublicKeys() throws Exception {
        HttpURLConnection connection = null;
        BufferedReader reader = null;
        try {
            URL url = new URL(APPLE_PUBLIC_KEY_URL);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(5000); // 5秒连接超时
            connection.setReadTimeout(5000);  // 5秒读取超时

            int responseCode = connection.getResponseCode();
            if (responseCode != 200) {
                throw new IOException("Failed to fetch Apple public keys: HTTP " + responseCode);
            }

            // 读取响应内容
            reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }

            // 解析 JSON
            ObjectMapper objectMapper = new ObjectMapper();
            Map<String, Object> responseMap = objectMapper.readValue(response.toString(), Map.class);
            return (List<Map<String, Object>>) responseMap.get("keys");
        } finally {
            if (reader != null) reader.close();
            if (connection != null) connection.disconnect();
        }
    }

    /**
     * 解析 Apple 公钥 JSON 数据为 PublicKey
     */
    private static PublicKey parsePublicKey(Map<String, Object> keyInfo) throws Exception {
        // Base64 URL 解码（Apple 使用 Base64URL）
        byte[] nBytes = Base64.getUrlDecoder().decode((String) keyInfo.get("n"));
        byte[] eBytes = Base64.getUrlDecoder().decode((String) keyInfo.get("e"));

        // 转换为 BigInteger（无符号）
        BigInteger modulus = new BigInteger(1, nBytes);
        BigInteger exponent = new BigInteger(1, eBytes);

        // 生成 RSA 公钥
        RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(spec);
    }
}
